﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Media;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace Patcher.Main
{
    public static class Functions
    {

        private static SoundPlayer SoundPlayer = new SoundPlayer();
        private static MD5CryptoServiceProvider CryptoService = new MD5CryptoServiceProvider();

        /// <summary>
        /// Converts a byte buffer to a managed type.
        /// </summary>
        public static T ConvertType<T>( byte[] bBuffer, CharSet eCharacterSet = CharSet.Unicode, int nLength = 2000 )
        {
            object tResult = null;
            Type tManagedType = typeof( T );
            
            if( tManagedType == typeof( byte[] ) )
                tResult = bBuffer;
            else if( tManagedType == typeof( bool ) )
                tResult = bBuffer[0] != 0;
            else if( tManagedType == typeof( byte ) )
                tResult = bBuffer[0];
            else if( tManagedType == typeof( char ) )
                tResult = (char) bBuffer[0];
            else if( tManagedType == typeof( short ) )
                tResult = BitConverter.ToInt16( bBuffer, 0 );
            else if( tManagedType == typeof( ushort ) )
                tResult = BitConverter.ToUInt16( bBuffer, 0 );
            else if( tManagedType == typeof( int ) )
                tResult = BitConverter.ToInt32( bBuffer, 0 );
            else if( tManagedType == typeof( uint ) )
                tResult = BitConverter.ToUInt32( bBuffer, 0 );
            else if( tManagedType == typeof( long ) )
                tResult = BitConverter.ToInt64( bBuffer, 0 );
            else if( tManagedType == typeof( ulong ) )
                tResult = BitConverter.ToUInt64( bBuffer, 0 );
            else if( tManagedType == typeof( float ) )
                tResult = BitConverter.ToSingle( bBuffer, 0 );
            else if( tManagedType == typeof( double ) )
                tResult = BitConverter.ToDouble( bBuffer, 0 );
            else if( tManagedType == typeof( string ) )
            {
                string sResult;
                if( eCharacterSet == CharSet.Auto || eCharacterSet == CharSet.Unicode )
                    sResult = Encoding.Unicode.GetString( bBuffer, 0, nLength );
                else
                    sResult = Encoding.ASCII.GetString( bBuffer, 0, nLength );

                int nNullIndex = sResult.IndexOf( "\0", StringComparison.Ordinal );
                if( nNullIndex == -1 )
                    tResult = sResult;
                else
                    tResult = sResult.Substring( 0, nNullIndex );
            }

            return (T) tResult;
        }
        
        /// <summary>
        /// Converts a managed type to a byte buffer.
        /// </summary>
        public static byte[] ConvertType<T>( T tData, CharSet eCharacterSet = CharSet.Unicode )
        {
            byte[] bResult = null;
            Type tManagedType = typeof( T );
            
            if( tManagedType == typeof( byte[] ) )
                bResult = (byte[])(object) tData;
            else if( tManagedType == typeof( bool ) )
                bResult = new[] { (bool)(object) tData ? (byte) 0x01 : (byte) 0x00 };
            else if( tManagedType == typeof( byte ) )
                bResult = new[] { (byte)(object) tData };
            else if( tManagedType == typeof( char ) )
                bResult = new[] { (byte)(char)(object) tData };
            else if( tManagedType == typeof( short ) )
                bResult = BitConverter.GetBytes( (short)(object) tData );
            else if( tManagedType == typeof( ushort ) )
                bResult = BitConverter.GetBytes( (ushort)(object) tData );
            else if( tManagedType == typeof( int ) )
                bResult = BitConverter.GetBytes( (int)(object) tData );
            else if( tManagedType == typeof( uint ) )
                bResult = BitConverter.GetBytes( (uint)(object) tData );
            else if( tManagedType == typeof( long ) )
                bResult = BitConverter.GetBytes( (long)(object) tData );
            else if( tManagedType == typeof( ulong ) )
                bResult = BitConverter.GetBytes( (ulong)(object) tData );
            else if( tManagedType == typeof( float ) )
                bResult = BitConverter.GetBytes( (float)(object) tData );
            else if( tManagedType == typeof( double ) )
                bResult = BitConverter.GetBytes( (double)(object) tData );
            else if( tManagedType == typeof( string ) )
            {
                if( eCharacterSet == CharSet.Auto || eCharacterSet == CharSet.Unicode )
                    bResult = Encoding.Unicode.GetBytes( (string)(object) tData );
                else
                    bResult = Encoding.ASCII.GetBytes( (string)(object) tData );
            }

            return bResult;
        }

        /// <summary>
        /// Converts a string to a managed type.
        /// </summary>
        public static T ConvertType<T>( string sString, CharSet eCharacterSet = CharSet.Unicode )
        {
            object tResult = null;
            Type tManagedType = typeof( T );

            if( tManagedType == typeof( string ) )
                tResult = sString;
            else if( tManagedType == typeof( bool ) )
                tResult = Convert.ToBoolean( sString );
            else if( tManagedType == typeof( byte ) )
                tResult = Convert.ToByte( sString );
            else if( tManagedType == typeof( char ) )
                tResult = Convert.ToChar( sString );
            else if( tManagedType == typeof( short ) )
                tResult = Convert.ToInt16( sString );
            else if( tManagedType == typeof( ushort ) )
                tResult = Convert.ToUInt16( sString );
            else if( tManagedType == typeof( int ) )
                tResult = Convert.ToInt32( sString );
            else if( tManagedType == typeof( uint ) )
                tResult = Convert.ToUInt32( sString );
            else if( tManagedType == typeof( long ) )
                tResult = Convert.ToInt64( sString );
            else if( tManagedType == typeof( ulong ) )
                tResult = Convert.ToUInt64( sString );
            else if( tManagedType == typeof( float ) )
                tResult = Convert.ToSingle( sString );
            else if( tManagedType == typeof( double ) )
                tResult = Convert.ToDouble( sString );
            else if( tManagedType == typeof( byte[] ) )
            {
                if( eCharacterSet == CharSet.Auto || eCharacterSet == CharSet.Unicode )
                    tResult = Encoding.Unicode.GetBytes( sString );
                else
                    tResult = Encoding.ASCII.GetBytes( sString );
            }

            return (T) tResult;
        }

        /// <summary>
        /// Plays a wave sound file.
        /// </summary>
        public static void PlaySound( string sFileName )
        {
            if( !File.Exists( sFileName ) || !sFileName.EndsWith( ".wav" ) )
                return;

            SoundPlayer.SoundLocation = sFileName;
            SoundPlayer.Play();
        }

        /// <summary>
        /// Gets the MD5 hash of a file.
        /// </summary>
        public static string MD5( string sFilePath )
        {
            FileStream fsStream = null;

            try
            {
                fsStream = new FileStream( sFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
                byte[] bHashValue = CryptoService.ComputeHash( fsStream );
                fsStream.Close();

                return BitConverter.ToString( bHashValue ).Replace( "-", "" );
            }
            catch
            {
                if( fsStream != null )
                    fsStream.Close();

                return "00000000000000000000000000000000";
            }
        }

        /// <summary>
        /// An ugly way to be able to access 64-bit process modules from a 32-bit process.
        /// </summary>
        public static string GetMainModulePath( Process pProcess )
        {
            string sQueryString = "SELECT ProcessId, ExecutablePath FROM Win32_Process WHERE ProcessId = " + pProcess.Id;
            using( ManagementObjectSearcher pManagerSearcher = new ManagementObjectSearcher( sQueryString ) )
            {
                using( ManagementObjectCollection liResults = pManagerSearcher.Get() )
                {
                    ManagementObject pManager = liResults.Cast<ManagementObject>().FirstOrDefault();
                    if( pManager != null )
                        return (string) pManager["ExecutablePath"];
                }
            }

            throw new Exception( "Functions.GetMainModulePath: Could not obtain process executable path!" );
        }

    }
}
